%%%-------------------------------------------------------------------
%%% @author liuwentao
%%% @doc
%%%     客户端登入
%%% @end
%%% Created : 08. 7月 2021 15:37
%%%-------------------------------------------------------------------
-module(client_rpc).
-author("liuwentao").

-export([
    handle/3
]).

-include("common.hrl").
-include("role.hrl").
-include("conn.hrl").
-include("group.hrl").


%% 查询角色列表
handle(1100, {}, Conn = #conn{account = Account, platform = P}) ->
    L =
        case role_data:list(Account, P) of
            L1 = [_ | _] -> L1;
            _ -> []
        end,
    {reply, {Account, L}, Conn#conn{}};


%% 创建角色,
handle(1110, {_, _, _, _}, #conn{need_cdkey = ?true}) ->
    {reply, return_false(?T("需要先激活账号才能创建角色"))};
handle(1110, {"", _, _, _}, #conn{}) ->
    {reply, return_false(?T("角色名不能为空"))};
handle(1110, {_, _, _, _}, #conn{pid_object = ObjectPid}) when ObjectPid =/= undefined ->
    {reply, return_false(?T("当前客户端连接进程已经关联了某个角色，不能重复登录"))};
handle(1110, {Name, Sex, Classes, Z}, Conn = #conn{account = Acc, socket = Socket, ip = Ip, port = Port, platform = P, channel_reg = ChannelReg}) ->
    Link = #link{socket = Socket, conn_pid = self(), ip = Ip, port = Port},
    %% 检查名字
    case util:check_name(Name) of
        {false, Reason} -> {reply, return_false(Reason)};
        _ ->
            case role_data:get_role_list(by_acc, Acc, P, Z) of
                {ok, RoleList} when length(RoleList) > 1 ->
                    {reply, return_false(?T("当前账号的已经创建有角色!"))};
                {ok, _} ->
                    %% 占有一个新的角色名
                    case role_data:create(Acc, P, Z, Name, Sex, Classes, self()) of
                        {ok, Role} ->
                            NRole = Role#role{channel_reg = ChannelReg, time_reg = date:unixtime()},
                            case role_data:create_save(NRole) of
                                {false, Reason} ->
                                    {reply, return_false(Reason)};
                                {ok, Rid} ->
                                    role_start(NRole#role{id = {Rid, P, Z}}, Link, Conn, ?false)
                            end;
                        {false, Reason} ->
                            {reply, return_false(Reason)};
                        {error, Err} ->
                            ?ERR("创建角色[account:~ts, platform:~ts, zone_id:~w]时发生异常: ~w", [Acc, P, Z, Err]),
                            {reply, return_false(?T("暂时无法创建新的角色，请稍后再试"))}
                    end;
                {error, Err} -> %% 查询角色时发生异常
                    ?ERR("角色[account:~ts, platform:~ts, zone_id:~w]登录时发生异常: ~w", [Acc, P, Z, Err]),
                    {reply, return_false(?T("角色登录系统暂时不可用，请稍后再试"))}
            end
    end;


%% 登录角色(除了本区角色外，同时可以登录已合服的其它区的角色)
handle(1120, {_Z, _Rid, _P}, #conn{need_cdkey = ?true}) ->
    {reply, return_false(?T("当前账号需要激活才能登陆角色"))};
handle(1120, {_Z, _Rid, _P}, #conn{pid_object = ObjectPid}) when ObjectPid =/= undefined ->
    {reply, return_false(?T("当前客户端连接进程已经关联了某个角色，不能重复登录"))};
handle(1120, {Z, Rid, P}, Conn = #conn{account = Acc, socket = Socket, ip = Ip, port = Port}) ->
    Link = #link{socket = Socket, conn_pid = self(), ip = Ip, port = Port},
    case role_query:local_pid(by_id, Rid, P, Z) of
        {ok, Pid} when is_pid(Pid) -> %% 角色已经在线
            role_switch(Pid, Link, Conn);
        {error, not_found} -> %% 角色不在线
            case role_data:local_fetch(by_id, Rid, P, Z) of
                {ok, Role} ->
                    %% 封装时间信息
                    role_start(Role, Link, Conn, ?true);
                {error, role_data_not_found} ->
                    {reply, return_false(?T("当前区服没有角色"))};
                {error, Err} ->
                    ?ERR("加载角色数据[account:~ts, platform:~ts, zone_id:~w]时发生异常: ~w", [Acc, P, Z, Err]),
                    {reply, return_false(?T("加载角色数据时发生异常"))}
            end;
        {error, Err} -> %% 查询角色时发生异常
            ?ERR("角色[account:~ts, platform:~ts, zone_id:~w]登录时发生异常: ~w", [Acc, P, Z, Err]),
            {reply, return_false(?T("角色登录系统暂时不可用，请稍后再试"))};
        _Err ->
            ?ERR("角色[account:~ts, platform:~ts, zone_id:~w]登录时发生异常: ~w", [Acc, P, Z, _Err]),
            {reply, return_false(?T("角色登录系统暂时不可用，请稍后再试"))}
    end;

%% 容错匹配
handle(_Cmd, _Data, _Role) ->
    ?DEBUG("模块[~w]收到无效的RPC调用[~w]: ~w", [?MODULE, _Cmd, _Data]),
    {error, unknow_command}.

%% ----------------------------------------------------
%% 私有函数
%% ----------------------------------------------------

%% 接管角色
role_switch(Pid, Link, Conn) ->
    case role:apply(sync, Pid, {fun fix_link/3, [Link, Conn]}) of
        {true, Return} ->
            server_conn:pack_send(self(), 1120, Return),
            {ok, Conn#conn{object = role, pid_object = Pid}};
        {false, Return} ->
            {reply, Return};
        _ERR ->
            ?INFO("_ERR:~w Pid ~w", [_ERR, Pid]),
            {reply, return_false(?T("登录角色失败，请稍后重试"))}
    end.

%% 启动角色进程
role_start(Role = #role{id = Rid, name = Name}, Link = #link{ip = Ip}, Conn, _Push) ->
    case role:start(Role#role{link = Link, ip_last_login = Ip}) of
        {ok, Pid} ->
            erlang:link(Pid),
            {reply, return_true("", Role), Conn#conn{object = role, pid_object = Pid}};
        {error, {already_started, _}} ->
            LocalName = role:get_proc_name(Rid),
            erlang:send(LocalName, {disconnect, normal_exit}),
            ?ERR("Name ~ts[~w]已经在线,不能重复登录", [Name, Rid]),
            {reply, return_false(?T("角色已经在线，不能重复登录"))};
        Err ->
            ?ERR("启动角色进程时发生异常: ~w", [Err]),
            {reply, return_false(?T("启动角色时发生异常"))}
    end.

%% @doc 替换角色绑定的连接器(用户顶号操作)
-spec fix_link(Role, NewLink, Conn) -> {ok, Reply, NewRole} when
    Role :: #role{},
    NewLink :: #link{},
    Conn :: #conn{},
    Reply :: boolean(),
    NewRole :: #role{}.
fix_link(Role = #role{name = _Name, pid = Pid, link = #link{conn_pid = ConnPid}}, NewLink = #link{conn_pid = NewConnPid, ip = NewLoginIp}, _Conn) ->
    ?DEBUG("角色[~ts]顶号上线", [_Name]),
    NewRole = Role#role{link = NewLink, ip_last_login = NewLoginIp},
    %% 离开分组
    role_group:leave(world, Pid),
    %% 重新加入分组
    role_group:join(world, #member{pid = Pid, conn_pid = NewConnPid}),
    erlang:unlink(ConnPid),
    erlang:link(NewConnPid),
    put(conn_pid, NewConnPid),
    %% 清除定时器，如果有的话
    case erase(disconnect_status) of
        {_, _, Ref} -> erlang:cancel_timer(Ref);
        _ -> ignore
    end,
    spawn(
        fun() ->
            server_conn:pack_send(ConnPid, 9901, {1, ?T("当前角色已经在别处登录，你被迫下线。")}),
            util:sleep(500),
            %% 强制关闭之前的连接器
            erlang:exit(ConnPid, kill)
        end
    ),
    {ok, {true, return_true("", NewRole)}, NewRole};
fix_link(_Role, _NewLink, _Conn) ->
    role:stop(async, _Role#role.pid, ?T("您的角色已经在别处登录了!")),
    {ok, {false, return_false(?T("角色正在登录，请重试一下"))}}.

%% -------------------------------
%% 额外公共返回
%% -------------------------------
return_false(Msg) ->
    {?false, Msg, 0, "", 0, "", 0, 0}.
return_true(Msg, #role{id = {Id, Platform, ZoneId}, name = Name, lev = Lev, time_reg = TimeReg}) ->
    NewLev = max(1, Lev),
    {?true, Msg, Id, Platform, ZoneId, Name, NewLev, TimeReg}.
